#include <codecvt>
#include <locale>
#include <iostream>

#include "IUString.h"

class StringCodecLocaleImp:public IUStringUtils::StringCodec
{
public:
	StringCodecLocaleImp(const char* codecName):cv(new std::codecvt_byname<wchar_t, char, mbstate_t>(codecName))
	{
		
	}

	IUByteArray Encode(const IUString& str, bool *ok) override {
		if (ok) {
			*ok = true;
		}

		std::string s = cv.to_bytes(str);
		auto begin = (const unsigned char*)s.data();
		auto end = begin + s.size();
		return { begin,end };
	}

	IUString Decode(const IUByteArray& bta, bool *ok) override {

		if (ok) {
			*ok = true;
		}

		std::string str{(const char*)bta.data(),bta.size()};

		try
		{
			return cv.from_bytes(str);
		}
		catch (const std::exception& e)
		{
			if (ok) {
				*ok = false;
			}
		}

		return {};
	}

private:
	std::wstring_convert<std::codecvt_byname<wchar_t, char, mbstate_t>> cv;
};

class StringCodecUTF8Imp :public IUStringUtils::StringCodec
{
public:
	StringCodecUTF8Imp() {
	}

	IUByteArray Encode(const IUString& str, bool *ok) override {
		if (ok) {
			*ok = true;
		}
		std::string s = cv.to_bytes(str);
		auto begin = (const unsigned char*)s.data();
		auto end = begin + s.size();
		return { begin,end };
	}

	IUString Decode(const IUByteArray& bta, bool *ok) override {
		std::string str{ (const char*)bta.data(),bta.size() };
		try
		{
			return cv.from_bytes(str);
		}
		catch (const std::exception& e)
		{
			if (ok) {
				*ok = false;
			}
		}
	}

private:
	std::wstring_convert<std::codecvt_utf8<wchar_t>> cv;
};


IUStringUtils::StringCodecPtr IUStringUtils::CreateCodec(const char * codecName)
{
	std::string name = codecName;

	if (name == "local")
	{
#ifdef WIN32
		name = "gbk";
#endif // WIN32

#ifdef __linux__
		name = "utf-8";
#endif
	}

	if (name == "utf-8")
	{
		return std::make_shared<StringCodecUTF8Imp>();
	}
	else if (name == "gbk")
	{
#ifdef WIN32
		return std::make_shared<StringCodecLocaleImp>("CHS");
#endif // WIN32

#ifdef __linux__
		return std::make_shared<StringCodecLocaleImp>("zh_CN.GBK");
#endif
	}

	return NULL;
}

IUByteArray IUStringUtils::Encode(const IUString & str, const char * codecName)
{
	auto codec = CreateCodec(codecName);

	return codec->Encode(str);
}

IUString IUStringUtils::Decode(const IUByteArray & bta, const char * codecName)
{
	auto codec = CreateCodec(codecName);

	return codec->Decode(bta);
}
